package med.movyzer.mobile.services;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;

import med.movyzer.mobile.datastructs.MovementSeries;
import med.movyzer.mobile.util.ExtCalendar;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Environment;
import android.os.IBinder;
import android.os.PowerManager;


public class AccelService extends Service implements SensorEventListener{
	public static AccelService service;
	public static final int INTERVAL=100;	//Time in ms
	
	private static final Class[] mStartForegroundSignature = new Class[] {
	    int.class, Notification.class};
	private static final Class[] mStopForegroundSignature = new Class[] {
	    boolean.class};
	public static final int NOTIFICATION_ID=42;	//Time in ms

	private NotificationManager mNM;
	private Method mStartForeground;
	private Method mStopForeground;
	private Object[] mStartForegroundArgs = new Object[2];
	private Object[] mStopForegroundArgs = new Object[1];

	public MovementSeries movements;
	public int timerRate;

	private PowerManager pm;
	private PowerManager.WakeLock wl;
	private SensorManager mSensorEventManager;
	private Sensor mSensor;
	private Float[] newValues;
	private Timer timer;
	private Timer saveTimer;
	
	private File file=null;

	@Override
	public IBinder onBind(Intent arg0) {
		return null;
	}


	public void onCreate() {
		super.onCreate();  
		
		mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
		try {
			mStartForeground = getClass().getMethod("startForeground",
					mStartForegroundSignature);
			mStopForeground = getClass().getMethod("stopForeground",
					mStopForegroundSignature);
		} catch (NoSuchMethodException e) {
			// Running on an older platform.
			mStartForeground = mStopForeground = null;
		}
		startForegroundCompat(NOTIFICATION_ID, new Notification());

		mSensorEventManager = (SensorManager) getApplicationContext().getSystemService(Context.SENSOR_SERVICE);
		mSensor = mSensorEventManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
		if(mSensor==null){
			return;
		}

		mSensorEventManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_FASTEST);
		service=this;

		movements=new MovementSeries();
		movements.setStartTime(new ExtCalendar());

		newValues=new Float[]{0f,0f,9.7f};
		timer = new Timer();

		pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
		wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AccelWL");
		wl.acquire();
	}

	public void onDestroy(){
		super.onDestroy();
	    stopForegroundCompat(NOTIFICATION_ID);
	}


	@Override
	public void onAccuracyChanged(Sensor arg0, int arg1) {
	}

	public void startTimer(int rate){
		timer.scheduleAtFixedRate(new TimerTask() {

			@Override
			public void run() {
				addSensorData();
			}

		}, rate, rate);
		timerRate=rate;
		
		saveTimer=new Timer();
		saveTimer.scheduleAtFixedRate(new TimerTask() {

			@Override
			public void run() {
				partiallySaveData();
			}

		}, 600000, 600000);
	}

	public void addSensorData(){


		movements.addMovement(newValues);
		//Log.i("tag", "logging "+intensity+" "+movements.size());
	}

	@Override
	public void onSensorChanged(SensorEvent event) {

		newValues=new Float[event.values.length];
		for(int i=0;i<event.values.length && i<10;i++){
			newValues[i]=event.values[i];	
		}
	}

	public int partiallySaveData(){
		boolean mExternalStorageAvailable = false;
		boolean mExternalStorageWriteable = false;
		String state = Environment.getExternalStorageState();
		if (Environment.MEDIA_MOUNTED.equals(state)) {
			// We can read and write the media
			mExternalStorageAvailable = mExternalStorageWriteable = true;
		} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
			// We can only read the media
			mExternalStorageAvailable = true;
			mExternalStorageWriteable = false;
			return -1;
		} else {
			// Something else is wrong. It may be one of many other states, but all we need
			//  to know is we can neither read nor write
			mExternalStorageAvailable = mExternalStorageWriteable = false;
			return -1;
		}
		
		File path=Environment.getExternalStorageDirectory();
		path.mkdirs();
		
		if(file==null){
			File newpath=new File(path, "download");
			newpath.mkdirs();
			path=newpath;
			
			file=new File(path.getPath(), "Movyzer_1.csv_buff");
		}
		
		FileWriter p;
		try {
			p=new FileWriter(file,true);

			ArrayList<Float[]> list=movements.getList();
			while(list.size()>0){
				for(int i=0;i<list.get(0).length;i++){
					p.write(list.get(0)[i].toString());
					if(i<list.get(0).length-1){
						p.write(",");
					}
				}
				p.write("\n");
				list.remove(0);
			}

			p.close();
		} catch (Exception e) {
			e.printStackTrace();
		} 
		return 1;
		
	}
	
	public int saveMovements(){
		
		wl.release();
		timer.cancel();
		saveTimer.cancel();
		mSensorEventManager.unregisterListener(this);

		partiallySaveData();

		boolean mExternalStorageAvailable = false;
		boolean mExternalStorageWriteable = false;
		String state = Environment.getExternalStorageState();
		if (Environment.MEDIA_MOUNTED.equals(state)) {
			// We can read and write the media
			mExternalStorageAvailable = mExternalStorageWriteable = true;
		} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
			// We can only read the media
			mExternalStorageAvailable = true;
			mExternalStorageWriteable = false;
			return -1;
		} else {
			// Something else is wrong. It may be one of many other states, but all we need
			//  to know is we can neither read nor write
			mExternalStorageAvailable = mExternalStorageWriteable = false;
			return -1;
		}
		
		File path=Environment.getExternalStorageDirectory();
		path.mkdirs();

		movements.setEndTime(new ExtCalendar());
		
		File newpath=new File(path, "download");
		newpath.mkdirs();
		path=newpath;

		File newFile=new File(path.getPath(), "Movyzer_1.csv");
		for(int i=2;i<10000 && newFile.exists();i++){
			newFile=new File(path, "Movyzer_"+i+".csv");
		}		

		PrintWriter p;
		try {
			p=new PrintWriter(newFile);
			BufferedReader reader=new BufferedReader(new FileReader(file));
			p.println("------------ Data File Created By Movyzer (mobile) -----------");
			p.println("Serial Number: AndroidMobilePhone");
			p.println("Start Time "+movements.getStartTime().getHMS());
			p.println("Start Date "+movements.getStartTime().getShortDate());
			p.println("Epoch Period (ms) "+timerRate);
			p.println("Download Time "+movements.getEndTime().getHMS());
			p.println("Download Date "+movements.getEndTime().getShortDate());
			p.println("Current Memory Address: 0");
			p.println("Current Battery Voltage: 0.0     Mode = 0");
			p.println("--------------------------------------------------");
			
			String line=reader.readLine();
			while(line!=null){
				p.println(line);
				line=reader.readLine();
			}	
			reader.close();
			file.delete();
			p.close();
		} catch (Exception e) {
			e.printStackTrace();
		} 

		
		return 1;
	}


	/**
	 * This is a wrapper around the new startForeground method, using the older
	 * APIs if it is not available.
	 */
	void startForegroundCompat(int id, Notification notification) {
	    // If we have the new startForeground API, then use it.
	    if (mStartForeground != null) {
	        mStartForegroundArgs[0] = Integer.valueOf(id);
	        mStartForegroundArgs[1] = notification;
	        try {
	            mStartForeground.invoke(this, mStartForegroundArgs);
	        } catch (InvocationTargetException e) {
	            // Should not happen.
	            System.out.println("Unable to invoke startForeground\n"+e.getMessage());
	        } catch (IllegalAccessException e) {
	            // Should not happen.
	            System.out.println("Unable to invoke startForeground\n"+e.getMessage());
	        }
	        return;
	    }

	    // Fall back on the old API.
	    setForeground(true);
	    mNM.notify(id, notification);
	}

	/**
	 * This is a wrapper around the new stopForeground method, using the older
	 * APIs if it is not available.
	 */
	void stopForegroundCompat(int id) {
	    // If we have the new stopForeground API, then use it.
	    if (mStopForeground != null) {
	        mStopForegroundArgs[0] = Boolean.TRUE;
	        try {
	            mStopForeground.invoke(this, mStopForegroundArgs);
	        } catch (InvocationTargetException e) {
	            // Should not happen.
	            System.out.println("Unable to invoke stopForeground\n"+e.getMessage());
	        } catch (IllegalAccessException e) {
	            // Should not happen.
	            System.out.println("Unable to invoke stopForeground\n"+e.getMessage());
	        }
	        return;
	    }

	    // Fall back on the old API.  Note to cancel BEFORE changing the
	    // foreground state, since we could be killed at that point.
	    mNM.cancel(id);
	    setForeground(false);
	}

	
}
